const _ = require("lodash");
const got = require("got");
const jwt = require("jsonwebtoken");
const router = require("koa-router")();
/**
 * api {get} /home/app/schedule/list 查询计划任务列表
 *
 * apiParam {Number} page 页码
 *
 */
router.get("/schedule/page/:page", async (ctx, next) => {
  const user = ctx.user;
  const baas = ctx.baas;
  const page = parseInt(ctx.params.page) < 1 ? 1 : parseInt(ctx.params.page);
  const schedules = await BaaS.Models.schedule
    .query({ where: { baas_id: baas.id } })
    .fetchPage({
      pageSize: ctx.config.pageSize,
      page: page,
      withRelated: ["class", "function"]
    });
  const userBaas = await BaaS.Models.user_baas
    .query({ where: { baas_id: baas.id } })
    .fetch();
  // 是否开启云函数
  Object.assign(schedules, { engine: userBaas.engine });
  ctx.success(schedules);
});
/**
 * api {get} /home/app/engine/list 查询计划任务
 *
 * apiParam {Number} :id 计划任务id
 *
 */
router.get("/schedule/id/:id", async (ctx, next) => {
  const user = ctx.user;
  const baas = ctx.baas;
  const id = ctx.params.id;
  const schedules = await BaaS.Models.schedule
    .query({ where: { id: id } })
    .fetch({ withRelated: ["class", "function"] });
  if (schedules) {
    ctx.success(schedules);
  } else {
    ctx.error("暂无相关信息");
  }
});
/**
 * api {post} /home/app/engine/schedule/add 添加提交计划任务
 *
 * apiParam {Number} classId 分组id
 * apiParam {Number} functionId 方法id
 * apiParam {String} rule cron函数
 *
 */
router.post("/schedule/add", async (ctx, next) => {
  const baas = ctx.baas;
  const { id, classId, functionId, rule } = ctx.post;
  // 验证表单是否为空
  const isEmpty = ctx.isEmpty(
    { classId: classId, functionId: functionId, rule: rule },
    ["classId", "functionId", "rule"]
  );
  if (!isEmpty) {
    ctx.error("请完善表单");
    return;
  }
  // 验证cron表达式
  const regExp = /^[1-5]?[0-9|*][\s][1-9]|1[0-9]|2[0-3]|[*][\s][1-9]|[1-2][0-9]|3[0-1]|[*][1-9]|1[0-2]|[*][\s][0-7|*]$/gi;
  if (rule == "* * * * *" || !regExp.test(rule)) {
    ctx.error("cron表达式格式不正确");
    return;
  }
  // 查询该分组下是否有这个函数
  const classFunction = await BaaS.Models.function
    .query(qb => {
      qb.where("baas_id", "=", baas.id);
      qb.where("class_id", "=", classId);
    })
    .fetch();
  if (!classFunction) {
    ctx.error("该分组下没有这个函数");
    return;
  }
  // 查询是否存在相同的计划任务
  const exist = await BaaS.Models.schedule
    .query(qb => {
      qb.where("baas_id", "=", baas.id);
      qb.where("class_id", "=", classId);
      qb.where("function_id", "=", functionId);
    })
    .fetch();
  if (exist && exist.id != id) {
    ctx.error("已存在相同的计划任务");
    return;
  }
  const result = await BaaS.Models.schedule
    .forge({
      id: id,
      baas_id: baas.id,
      class_id: classId,
      function_id: functionId,
      rule: rule
    })
    .save({
      withRedisKey: `baas:*:appid:${baas.appid}:appkey:${
        baas.appkey
      }:class:*:function:${classFunction.name}:schedule`
    });
  ctx.success(result);
});
/**
 * api {post} /home/app/engine/schedule/switch 修改计划任务
 *
 * apiParam {Number} id 云函数id
 * apiParam {Number} status 计划任务修改状态
 *
 */
router.post("/schedule/switch", async (ctx, next) => {
  const baas = ctx.baas;
  const type = ctx.params.type;
  const { id, status } = ctx.post;
  const key = `id:${id}:status${status}`;
  const locked = await BaaS.redis.lock(key);
  if (locked) {
    // 查询计划任务信息
    const schedule = await BaaS.Models.schedule
      .query({ where: { id: id } })
      .fetch({ withRelated: ["function"] });
    const classInfo = await BaaS.Models.class
      .query({ where: { id: schedule.class_id } })
      .fetch();
    const classAuth = await BaaS.Models.class_auth
      .query({ where: { class_id: schedule.class_id } })
      .fetchAll({ withRelated: ["class"] });
    const header = {};
    // 循环在header里加验证参数
    for (const key in classAuth) {
      const auth = await BaaS.Models.auth
        .query({ where: { id: classAuth[key].auth_id } })
        .fetch();
      const encode = jwt.sign({ value: ctx.randomCode(4) }, auth.secret, {
        expiresIn: auth.expires
      });
      const objKey = auth.name;
      Object.assign(header, { [objKey]: encode });
    }
    const className = classInfo.name;
    Object.assign(
      header,
      { "x-qingful-appid": baas.appid },
      { "x-qingful-appkey": baas.appkey }
    );

    // 停止计划任务
    const url =
      ctx.config("baas.url") +
      `class/${className}/function/${schedule.function.name}/schedule/${
        status == 1 ? "start" : "cancel"
      }`;
    // const url = `http://192.168.31.13:3002/class/${className}/function/${
    //   schedule.function.name
    // }/schedule/${status == 1 ? "start" : "cancel"}`;
    await got(url, {
      headers: header
    });
    // 判断修改状态;
    const switchAdd = await BaaS.Models.schedule
      .forge({
        id: id,
        status: status
      })
      .save({
        withRedisKey: `baas:*:appid:${baas.appid}:appkey:${
          baas.appkey
        }:class:*:function:${schedule.function.name}:schedule`
      });
    if (switchAdd) {
      ctx.success(switchAdd);
    } else {
      ctx.error("修改失败");
      return;
    }
    await BaaS.redis.unlock(key);
  } else {
    ctx.error("请稍后重试");
    console.log(`${key} Waiting`);
    return;
  }
});
/**
 * api {post} /home/app/engine/schedule/delete 删除计划任务
 *
 * apiParam {Number} id 云函数id
 *
 */
router.post("/schedule/delete", async (ctx, next) => {
  const baas = ctx.baas;
  const type = ctx.params.type;
  const { id } = ctx.post;
  const key = `id:${id}`;
  const locked = await BaaS.redis.lock(key);
  if (locked) {
    // 查询计划任务信息
    const schedule = await BaaS.Models.schedule
      .query({ where: { id: id } })
      .fetch({ withRelated: ["function"] });
    const classInfo = await BaaS.Models.class
      .query({ where: { id: schedule.class_id } })
      .fetch();
    const classAuth = await BaaS.Models.class_auth
      .query({ where: { class_id: schedule.class_id } })
      .fetchAll({ withRelated: ["class"] });
    const header = {};
    // 循环在header里加验证参数
    for (const key in classAuth) {
      const auth = await BaaS.Models.auth
        .query({ where: { id: classAuth[key].auth_id } })
        .fetch();
      const encode = jwt.sign({ value: ctx.randomCode(4) }, auth.secret, {
        expiresIn: auth.expires
      });
      const objKey = auth.name;
      Object.assign(header, { [objKey]: encode });
    }
    const className = classInfo.name;
    Object.assign(
      header,
      { "x-qingful-appid": baas.appid },
      { "x-qingful-appkey": baas.appkey }
    );
    // 先停止计划任务
    const url =
      ctx.config("baas.url") +
      `class/${className}/function/${schedule.function.name}/schedule/cancel`;
    // const url = `http://192.168.31.13:3002/class/${className}/function/${
    //   schedule.function.name
    // }/schedule/cancel`;
    await got(url, {
      headers: header
    });
    // 删除计划任务
    const result = await BaaS.Models.schedule
      .forge({
        id: id,
        baas_id: baas.id
      })
      .destroy();
    if (result) {
      ctx.success(result);
    } else {
      ctx.error("删除失败");
      return;
    }
    await BaaS.redis.unlock(key);
  } else {
    ctx.error("请稍后重试");
    console.log(`${key} Waiting`);
    return;
  }
});
/**
 * api {get} /home/app/engine/schedule/:classId 获取该分组下的函数
 *
 * apiParam {Number} classId 分组id
 *
 */
router.get("/schedule/classId/:classId", async (ctx, next) => {
  const baas = ctx.baas;
  const classId = ctx.params.classId;
  // 获取分组id列表，判断是否正确
  const classList = await BaaS.Models.class
    .query({ where: { baas_id: baas.id } })
    .fetchAll();
  const classIds = _.map(classList, "id");
  if (!classIds.includes(parseInt(classId))) {
    ctx.error("分组id错误");
    return;
  }
  // 查询该分组下的所有方法
  const functions = await BaaS.Models.function
    .query(qb => {
      qb.where("baas_id", "=", baas.id);
      qb.where("class_id", "=", classId);
    })
    .fetchAll();

  ctx.success(functions);
});

module.exports = router;
